﻿//---------------------------------------------------------------------
//Copyright (c) 2009, Pierre Milet
//All rights reserved.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
//OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
//TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
//---------------------------------------------------------------------
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Net;
using System.Collections;
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Linq;

using Microsoft.BizTalk.Streaming;
using Microsoft.BizTalk.TransportProxy.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.Samples.BizTalk.Adapter.Common;
using Microsoft.XLANGs.BaseTypes;

namespace BizMock.Adapter.Runtime
{
    /// <summary>
    /// There is one instance of BizMockTransmitterEndpoint class for each every static send port.
    /// Messages will be forwarded to this class by AsyncTransmitterBatch (via BizMockTransmitAdapterBatch)
    /// </summary>
    internal class BizMockTransmitterEndpoint: AsyncTransmitterEndpoint
    {
        private IBaseMessage		solicitMsg			= null;
        private IBTTransportProxy	transportProxy		= null;
        private AsyncTransmitter	asyncTransmitter	= null;
        private string propertyNamespace;
        private string _bizMockFileInterchangePath;
        public BizMockTransmitterEndpoint(AsyncTransmitter asyncTransmitter) : base(asyncTransmitter)
        {
            this.asyncTransmitter = asyncTransmitter;
            this.transportProxy = asyncTransmitter.TransportProxy;
            this._bizMockFileInterchangePath = string.Format("{0}\\{1}", System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"BizMock");

        }

        public override void Open(EndpointParameters endpointParameters, IPropertyBag handlerPropertyBag, string propertyNamespace)
        {
            this.propertyNamespace = propertyNamespace;
        }

        /// <summary>
        /// Implementation for AsyncTransmitterEndpoint::ProcessMessage
        /// Transmit the message and optionally return the response message
        /// </summary>
        public override IBaseMessage ProcessMessage(IBaseMessage message)
        {
            this.solicitMsg = message;

            BizMockTransmitProperties props = new BizMockTransmitProperties(message, this.propertyNamespace);
            IBaseMessage responseMsg = null;
            
            if ( props.IsTwoWay )
            {
                responseMsg = sendRequestMessage(this.solicitMsg, props);
                responseMsg.Context = message.Context; 
            }
            else
            {
				responseMsg = sendRequestMessage(this.solicitMsg, props);

                // Unexpected response (for OneWay operation) - Ignore the response;
                // Ensure its streams are still properly closed.
                if (responseMsg != null)
                {
                    for (int i = 0; i < responseMsg.PartCount; i++)
                    {
                        string partName;
                        var part = responseMsg.GetPartByIndex(i, out partName);
                        part.GetOriginalDataStream().Close();
                    }

                    responseMsg = null;
                }
            }

            return responseMsg;
        }

        private IBaseMessage sendRequestMessage(IBaseMessage msg, BizMockTransmitProperties config)
        {
            // Note: both the body part and the stream maybe null, so we need to 
            // check for them
            string charset = string.Empty;
            IBaseMessagePart bodyPart = msg.BodyPart;
            IBaseMessage responseMsg = null;

            if (null != bodyPart && (null != bodyPart.GetOriginalDataStream()))
			{
                TransmitBizMockRequest(config.Uri, msg.MessageID.ToString(), msg);

                if (config.IsTwoWay)
                {
                    responseMsg = ReadBizMockResponse(config.Uri, config.Timeout);
                }
                
			}
            return responseMsg;
        }

        private void TransmitBizMockRequest(string uri, string messageId, IBaseMessage message)
        {
            string portName = uri.Replace("BizMock://", "");

            MessageFileHelper.SaveMessage(message, _bizMockFileInterchangePath, portName, messageId, "Requestctx", "Requestmsg", "Requestctrl");
        }

        internal delegate bool BizMockConditionDelegate();
        private const int CHECK_INTERVAL = 200;
        internal static bool WaitForBizMockCondition(BizMockConditionDelegate d, int seconds)
        {
            TimeSpan waitTime = new TimeSpan(0, 0, seconds);
            long beginTicks = DateTime.Now.Ticks;
            long elapsedTicks = 0;
            bool timeOut = false;
            do
            {
                Thread.Sleep(CHECK_INTERVAL);
                elapsedTicks = DateTime.Now.Ticks - beginTicks;
                timeOut = waitTime.Ticks < elapsedTicks;
            } while (!d.Invoke() && !timeOut);
            return timeOut;
        }

        /// <summary>
        /// Reads the content of a BizMock Message to a IBaseMessage
        /// </summary>
        /// <param name="uri">port uri</param>
        /// <param name="messageType">type of message to read (Request, Response) </param>
        /// <param name="timeout">timeout in seconds to wait before to fail</param>
        private IBaseMessage ReadBizMockResponse(string uri, int timeout)
        {
            string searchPattern = string.Format("{0}_Responsectrl_*.xml", uri.Replace("BizMock://", ""));

            bool exitByTimeOut = WaitForBizMockCondition(delegate() { return Directory.GetFiles(_bizMockFileInterchangePath, searchPattern).Length > 0; }, timeout);
            if (exitByTimeOut)
            {
                throw new BizMockTransmitAdapterException(string.Format("control file {0}\\{1}_Responsectrl_*.xml not found", _bizMockFileInterchangePath, uri.Replace("BizMock://", "")));
            }
            string[] ctrlFiles = Directory.GetFiles(_bizMockFileInterchangePath, searchPattern);

            string ctrlFilePath = ctrlFiles[0];
            IBaseMessage responseMsg = MessageFileHelper.LoadMessage(ctrlFilePath, transportProxy.GetMessageFactory());

            MessageFileHelper.RemoveBizMockMessage(ctrlFilePath);

            return responseMsg;
        }       
    }
}
